package cn.mst.vrs.service.auth;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.mst.vrs.controller.admin.auth.vo.AuthPageReqVO;
import cn.mst.vrs.controller.admin.auth.vo.AuthRespVO;
import cn.mst.vrs.controller.admin.auth.vo.AuthSaveReqVO;
import cn.mst.vrs.controller.app.auth.vo.VrsUserLoginReqVO;
import cn.mst.vrs.controller.app.auth.vo.VrsUserRegisterReqVO;
import cn.mst.vrs.convert.AuthConvert;
import cn.mst.vrs.dal.dataobject.auth.AuthDO;
import cn.mst.vrs.dal.dataobject.authtype.AuthTypeDO;
import cn.mst.vrs.dal.dataobject.user.UserDO;
import cn.mst.vrs.dal.mysql.auth.VrsAuthMapper;
import cn.mst.vrs.dal.mysql.authtype.AuthTypeMapper;
import cn.mst.vrs.dal.mysql.user.VrsUserMapper;
import cn.mst.vrs.enums.GenderDictEnum;
import cn.mst.vrs.util.CryptoUtil;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.mst.vrs.enums.ErrorCodeConstants.*;

/**
 * 认证 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
public class AuthServiceImpl implements AuthService {

    @Resource
    private VrsAuthMapper vrsAuthMapper;

    @Resource
    private VrsUserMapper vrsUserMapper;

    @Resource
    private AuthTypeMapper authTypeMapper;

    @Override
    public Long createAuth(AuthSaveReqVO createReqVO) {
        // 插入
        AuthDO auth = BeanUtils.toBean(createReqVO, AuthDO.class);
        auth.setCredential(CryptoUtil.encrypt(auth.getCredential()));
        vrsAuthMapper.insert(auth);
        // 返回
        return auth.getId();
    }

    @Override
    public void updateAuth(AuthSaveReqVO updateReqVO) {
        // 校验存在
        validateAuthExists(updateReqVO.getId());
        // 更新
        AuthDO updateObj = BeanUtils.toBean(updateReqVO, AuthDO.class);
        updateObj.setCredential(CryptoUtil.encrypt(updateObj.getCredential()));
        vrsAuthMapper.updateById(updateObj);
    }

    @Override
    public void deleteAuth(Long id) {
        // 校验存在
        validateAuthExists(id);
        // 删除
        vrsAuthMapper.deleteById(id);
    }

    private void validateAuthExists(Long id) {
        if (vrsAuthMapper.selectById(id) == null) {
            throw exception(AUTH_NOT_EXISTS);
        }
    }

    @Override
    public AuthRespVO getAuth(Long id) {
        AuthDO authDO = vrsAuthMapper.selectById(id);
        if (ObjUtil.isNull(authDO)) {
            throw exception(AUTH_NOT_EXISTS);
        }
        Long userId = authDO.getUserId();
        UserDO userDO = vrsUserMapper.selectById(userId);
        if (ObjUtil.isNull(userDO)) {
            throw exception(USER_NOT_EXISTS);
        }
        AuthTypeDO authTypeDO = authTypeMapper.selectById(authDO.getTypeId());
        if (ObjUtil.isNull(authTypeDO)) {
            throw exception(AUTH_TYPE_NOT_EXISTS);
        }
        authDO.setCredential(CryptoUtil.decrypt(authDO.getCredential()));
        return AuthConvert.INSTANCE.toRespVO(authDO, userDO.getNickName(), authTypeDO.getCode());
    }

    @Override
    public PageResult<AuthRespVO> getAuthPage(AuthPageReqVO pageReqVO) {
        PageResult<AuthDO> pageResult = vrsAuthMapper.selectPage(pageReqVO);
        List<AuthDO> list = pageResult.getList();
        Set<Long> userIdSet = list.stream().map(AuthDO::getUserId).collect(Collectors.toSet());
        List<UserDO> userList = vrsUserMapper.selectBatchIds(userIdSet);
        HashMap<Long, UserDO> userMap = new HashMap<>();
        userList.forEach(user -> userMap.put(user.getId(), user));
        Set<Long> typeIdSet = list.stream().map(AuthDO::getTypeId).collect(Collectors.toSet());
        List<AuthTypeDO> typeList = authTypeMapper.selectBatchIds(typeIdSet);
        HashMap<Long, AuthTypeDO> typeMap = new HashMap<>();
        typeList.forEach(type -> typeMap.put(type.getId(), type));
        List<AuthRespVO> respList = list.stream().map(auth -> AuthConvert.INSTANCE.toRespVO(auth, userMap.get(auth.getUserId()).getNickName(), typeMap.get(auth.getTypeId()).getCode())).toList();
        return new PageResult<>(respList, pageResult.getTotal());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void registerVrs(VrsUserRegisterReqVO reqVO) {
        AuthTypeDO authType = validAuthTypeExistsByAuthTypeCode(reqVO.getAuthTypeCode());
        validIdentifierNotExistsPeerAuthType(reqVO.getIdentifier(), authType.getId());

        UserDO initUser = UserDO.builder()
                .nickName(genInitNickName())
                .avatar(getInitAvatar())
                .sign("暂无")
                .gender(GenderDictEnum.UNKNOWN.getGender())
                .birthday(LocalDate.of(1970, 1, 1))
                .creditworthy(100)
                .build();
        vrsUserMapper.insert(initUser);

        AuthDO auth = AuthDO.builder()
                .userId(initUser.getId())
                .typeId(authType.getId())
                .identifier(reqVO.getIdentifier())
                .credential(CryptoUtil.encrypt(reqVO.getCredential()))
                .build();
        vrsAuthMapper.insert(auth);
    }

    @Override
    public String loginVrs(VrsUserLoginReqVO reqVO) {
        AuthTypeDO authType = validAuthTypeExistsByAuthTypeCode(reqVO.getAuthTypeCode());
        validIdentifierExistsPeerAuthType(reqVO.getIdentifier(), authType.getId());

        AuthDO one = ChainWrappers.lambdaQueryChain(vrsAuthMapper)
                .eq(AuthDO::getTypeId, authType.getId())
                .eq(AuthDO::getIdentifier, reqVO.getIdentifier())
                .one();
        if (!StrUtil.equals(CryptoUtil.decrypt(one.getCredential()), reqVO.getCredential())) {
            throw exception(AUTH_CREDENTIAL_NOT_MATCH);
        }

        StpUtil.login(one.getId());

        return StpUtil.getTokenValue();
    }

    private String genInitNickName() {
        return "user_" + LocalDateTime.now().toEpochSecond(ZoneOffset.of("+08:00"));
    }

    private String getInitAvatar() {
        return "https://img.yzcdn.cn/vant/cat.jpeg";
    }

    private AuthTypeDO validAuthTypeExistsByAuthTypeCode(String authTypeCode) {
        AuthTypeDO authType = authTypeMapper.selectOne(AuthTypeDO::getCode, authTypeCode);
        if (ObjUtil.isNull(authType)) {
            throw exception(AUTH_TYPE_NOT_EXISTS);
        }
        return authType;
    }

    private void validIdentifierNotExistsPeerAuthType(String identifier, Long authTypeId) {
        AuthDO auth = vrsAuthMapper.selectOneBy(identifier, authTypeId);
        if (!ObjUtil.isNull(auth)) {
            throw exception(AUTH_EXISTS);
        }
    }

    private void validIdentifierExistsPeerAuthType(String identifier, Long authTypeId) {
        AuthDO auth = vrsAuthMapper.selectOneBy(identifier, authTypeId);
        if (ObjUtil.isNull(auth)) {
            throw exception(AUTH_NOT_EXISTS);
        }
    }

}